<script lang="ts">
type ModeType = 'past' | 'future'; // 模式类型 past=>过去 future=>将来

type FocusType = 'left' | 'right'; // 聚焦类型

type DataType = (string | undefined)[];

const today = formatDate(new Date(), 'yyyy-MM-dd');

// 转换
function turn(value: string): number {
  return parseDate(value).getTime();
}

// 范围
function range<T>(vlaue: number, callback: (_: never, i: number) => T) {
  return Array.from({ length: vlaue }, callback);
}

// 推断
function deduce(value: string, fill = true) {
  const [year, month, day] = value.split('-');
  const date = new Date(Number(year), Number(month), 0);
  const days = fill ? date.getDate() : Number(day);

  return range(days, (_, i) => i + 1);
}

// 计算
function calculate(mode: ModeType, value: string, gap: number) {
  let [year, month, _] = value.split('-');
  let [years, months, days]: number[][] = [[], [], []];

  switch (mode) {
    case 'past':
      // 处理年份
      years = range<number>(gap, (_, i) => Number(year) - gap + i + 1);
      // 处理月份
      months = range(Number(month), (_, index) => index + 1);
      // 处理日期
      days = deduce(value, false);
      break;

    case 'future':
      // 处理年份
      if (gap % 2 == 0) gap += 1;
      const avg = Math.floor(gap / 2);
      years = [
        ...range<number>(avg, (_, i) => Number(year) - avg + i),
        Number(year),
        ...range<number>(avg, (_, i) => Number(year) + i + 1),
      ];
      // 处理月份
      months = range(12, (_, index) => index + 1);
      // 处理日期
      days = deduce(value);
      break;
  }

  return [years, months, days];
}

// 格式化
function format(value: string) {
  return value
    .split('-')
    .map((item) => item.padStart(2, '0'))
    .join('-');
}
</script>

<script lang="ts" setup>
import { ref, watch, computed, onMounted } from 'vue';
import { parseDate, formatDate } from '@/utils/util';

import DateColumn from './date-column.vue';

const props = withDefaults(
  defineProps<{ mode?: ModeType; data?: DataType }>(),
  {
    mode: 'past',
    data: [],
  }
);

const emits = defineEmits<{
  (e: 'change', value: (string | undefined)[]): void;
}>();

const columnRefs = ref<unknown[]>([]); // 列引用

const focus = ref<FocusType>('left');

const count = ref(0); // 点击计数

const columns = ref<number[][]>([]); // 列数据

const position = ref<number[]>([]); // 当前位置

const result = ref<DataType>([]); // 结果

const start = computed(() => result.value[0]);

const end = computed(() => result.value[1]);

watch(count, () => {
  let _current: string;
  switch (focus.value) {
    case 'left':
      if (start.value === undefined) {
        result.value[0] = today;
      }
      _current = start.value!;
      break;
    case 'right':
      if (end.value === undefined) {
        result.value[1] = today;
      }
      _current = end.value!;
      break;
  }

  update(_current!);
});

watch(result, (value) => {
  if (value.length === 0) {
    update(today);
  } else {
    update(value[0]!);
  }
});

/**
 * 更新
 */
const update = (value: string, index?: number, reset = false) => {
  // 更新所选项月份下面的日期
  switch (props.mode) {
    case 'past':
      const [year, month, _] = today.split('-');
      if (turn(value) > turn(`${year}-${month}-01`)) {
        columns.value[2] = deduce(today, false);
      } else {
        columns.value[2] = deduce(value, true);
      }
      break;
    case 'future':
      columns.value[2] = deduce(value, true);
      break;
  }

  position.value = value.split('-').map((item, index) => {
    let find = columns.value[index].indexOf(Number(item));
    if (find !== -1) {
      return find;
    } else {
      return columns.value[index].length - 1;
    }
  });

  if (reset) {
    setTimeout(() => {
      columnRefs.value.forEach((el, _index) => {
        if (_index === index) el.computedOffset(position.value[index]);
      });
    }, 300);
  }
};

/**
 * 初始化
 */
const init = () => {
  columns.value = calculate(props.mode!, today, 5);

  result.value = props.data!;
};

/**
 * 焦点
 */
const handleFocus = (type: FocusType) => {
  focus.value = type;
  count.value += 1;
};

/**
 * 改变
 */
const handleChange = (index: number, value: number) => {
  position.value[index] = value;

  let _current: string = position.value.reduce((acc: string, cur, i) => {
    return acc + (i === 0 ? '' : '-') + columns.value[i][cur];
  }, '');

  switch (focus.value) {
    case 'left':
      if (end.value && turn(_current) > turn(end.value)) {
        _current = end.value;
      }
      result.value[0] = format(_current);

      break;
    case 'right':
      if (start.value && turn(_current) < turn(start.value)) {
        _current = start.value;
      }
      result.value[1] = format(_current);
      break;
  }

  update(_current!, index, true);

  emits('change', result.value);
};

/**
 * 判断是否焦点
 */
const isFocus = (type: FocusType) => {
  return type === focus.value;
};

onMounted(() => init());
</script>

<template>
  <div class="date-picker">
    <div class="header">
      <div
        class="header__value"
        @click="() => handleFocus('left')"
        :class="{ 'header__status--active': isFocus('left') }"
      >
        <template v-if="start"> {{ start }} </template>

        <template v-else>开始时间</template>
      </div>

      <div class="header__icon--line"></div>

      <div
        class="header__value"
        :class="{ 'header__status--active': isFocus('right') }"
        @click="() => handleFocus('right')"
      >
        <template v-if="end"> {{ end }} </template>

        <template v-else>结束时间</template>
      </div>
    </div>

    <div class="content">
      <section class="content__mask"></section>

      <template v-for="(column, index) in columns">
        <date-column
          :list="column"
          :value="position[index]"
          :ref="(el:unknown) => (columnRefs[index] = el)"
          @change="(value: number) => handleChange(index, value)"
        >
          <template #defualt="{ item }">
            <span v-if="index === 0">{{ item }}年</span>

            <span v-if="index === 1">{{ item }}月</span>

            <span v-if="index === 2">{{ item }}日</span>
          </template>
        </date-column>
      </template>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.date-picker {
  background-color: #fff;
}

.header {
  display: grid;
  grid-template-columns: 1fr 12px 1fr;
  gap: 0 16px;

  margin-bottom: 20px;

  &__value {
    padding: 0px 18px;
    height: 28px;
    background: #f5f5f5;
    border-radius: 14px;
    color: #9b9b9b;
    text-align: center;
    line-height: 28px;
    text-align: center;

    font-weight: 400;
    font-size: 12px;
    color: #666666;
  }

  &__icon {
    &--line {
      align-self: center;
      height: 2px;
      width: 100%;
      background-color: #c4c4c4;
    }
  }

  &__status {
    &--active {
      color: #333333 !important;
    }
  }
}

.content {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 0 16px;

  &__mask {
    box-sizing: border-box;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: 0;
    width: 100%;
    height: 6vh;
    background-color: #f9f9ff;
  }
}
</style>
